#include <sourcemod>
#include <sdktools>

#pragma semicolon 1

// Constants
#define PLUGIN_VERSION	 "1.04"

#define MESS			 "\x03[丫蛋] \x01%t"
#define DEBUG_MESS		 "\x04[丫蛋] \x01"

// Global vars
new Float:originSaves[MAXPLAYERS+1][3];
new Float:angleSaves[MAXPLAYERS+1][3];

new collisionGroup;

// ConVar-stuff
new Handle:sm_cpsaver_enable			 = INVALID_HANDLE;
new Handle:sm_cpsaver_version			 = INVALID_HANDLE;
new Handle:sm_cpsaver_block_falldamage	 = INVALID_HANDLE;
//new Handle:sm_cpsaver_standard_hp		 = INVALID_HANDLE;
new Handle:sm_cpsaver_noblock_enable	 = INVALID_HANDLE;
new Handle:sm_cpsaver_reset_checkpts	 = INVALID_HANDLE;
new Handle:sm_cpsaver_display_help		 = INVALID_HANDLE;
new Handle:sm_cpsaver_hp		 	 = INVALID_HANDLE;
new Handle:sm_cpsaver_hpb		 	 = INVALID_HANDLE;

public Plugin:myinfo =
{
	name = "坐标存点系统",
	author = "汉化一夜男",
	description = "A public checkpoint saving system for bhop/jump/etc servers",
	version = PLUGIN_VERSION,
	url = "http://www.dataviruset.com/"
};

public OnPluginStart()
{
	// Load translations
	LoadTranslations("cpsaver.phrases");

	// Console commands
	RegConsoleCmd("sm_cd", Command_Save);			//存点
	RegConsoleCmd("sm_dd", Command_Teleport);		//还原
	RegConsoleCmd("sm_cdcd", Command_CPmenu);		//菜单

	// Events hooks
	HookEvent("player_hurt", Event_PlayerHurt);
	HookEvent("player_spawn", Event_PlayerSpawn);
	HookEvent("player_death", Event_PlayerDeath);
	HookEvent("player_team", Event_PlayerTeam);
	//
	HookEvent("round_start", Event_RoundStart);// 回合轮换开始
	HookEvent("round_end", Event_RoundEnd);// 轮换结束

	// Create ConVars
	sm_cpsaver_enable = CreateConVar("sm_cpsaver_enable", "1", "插件开关: 0 - 关, 1 - 开");
	sm_cpsaver_version = CreateConVar("sm_cpsaver_version", PLUGIN_VERSION, "SM_CheckpointSaver plugin version (unchangeable)", FCVAR_PLUGIN|FCVAR_SPONLY|FCVAR_REPLICATED|FCVAR_NOTIFY);
	sm_cpsaver_block_falldamage = CreateConVar("sm_cpsaver_block_falldamage", "1", "开/关 blocking of fall damage (resets hp to sm_cpsaver_standard_hp after a player gets hurt from falling unless the fall damage is enough to kill him): 0 - 禁用, 1 - 启用");
	//sm_cpsaver_standard_hp = CreateConVar("sm_cpsaver_standard_hp", "300", "给幸存者附加血量: x - hp value");
	sm_cpsaver_noblock_enable = CreateConVar("sm_cpsaver_noblock_enable", "1", "开/关 player vs player collision blocking: 0 - 禁用, 1 - 启用");
	sm_cpsaver_reset_checkpts = CreateConVar("sm_cpsaver_reset_checkpts", "0", "When to reset checkpoints for a player: 0 - 禁用, 1 - on teamchange, 2 - on player death");
	sm_cpsaver_display_help = CreateConVar("sm_cpsaver_display_help", "1", "是否显示帮助: 0 - 不显示, 1 - 显示");
	sm_cpsaver_hp = CreateConVar("sm_cpsaver_hp", "5", "还原后血量损伤");
	sm_cpsaver_hpb = CreateConVar("sm_cpsaver_hpb", "1", "保存后血量损伤");
	
	SetConVarString(sm_cpsaver_version, PLUGIN_VERSION);
	//AutoExecConfig(true, "sm_cpsaver");

	// Hook ConVar-changes
	HookConVarChange(sm_cpsaver_version, VersionChange);
	HookConVarChange(sm_cpsaver_noblock_enable, NoblockChange);

	collisionGroup = FindSendPropOffs("CBaseEntity", "m_CollisionGroup");
	if (collisionGroup == -1)
	{
		SetFailState("[sm_cpsaver NoBlock] Failed to get offset for CBaseEntity::m_CollisionGroup.");
	}
}

public VersionChange(Handle:convar, const String:oldValue[], const String:newValue[])
{
	SetConVarString(convar, PLUGIN_VERSION);
}

public OnConfigsExecuted()
{
	InitPrecache();
}

public OnMapStart()
{
	/* Reset flags */
	for(new i = 1; i <= MAXPLAYERS; i++)
	{
		ResetCpSaves(i);
	}
	InitPrecache();
}

public OnMapEnd()
{
	for(new i = 1; i <= MAXPLAYERS; i++)
	{
		ResetCpSaves(i);
	}
}

InitPrecache()
{
	/* Precache model */
	PrecacheModel("models/props_junk/propanecanister001a.mdl", true);
	PrecacheModel("models/props_junk/gascan001a.mdl", true);

	/* Precache sound */
	PrecacheSound("ambient/spacial_loops/lights_flicker.wav", true);
	PrecacheSound("level/startwam.wav", true);
	PrecacheSound("weapons/awp/gunfire/awp1.wav", true);
	PrecacheSound("animation/bombing_run_01.wav", true);

	/* Precache particle */
	PrecacheParticle("gas_explosion_main");
	PrecacheParticle("electrical_arc_01_cp0");
	PrecacheParticle("electrical_arc_01_system");

	/* Precache sprite */
///	g_sprite = PrecacheModel("materials/sprites/laser.vmt");
}

public Event_RoundStart (Handle:event, const String:name[], bool:dontBroadcast)
{
	for(new i = 1; i <= MAXPLAYERS; i++)
	{
		ResetCpSaves(i);
	}
}
public Event_RoundEnd (Handle:event, const String:name[], bool:dontBroadcast)
{
	for(new i = 1; i <= MAXPLAYERS; i++)
	{
		ResetCpSaves(i);
	}
}

public NoblockChange(Handle:convar, const String:oldValue[], const String:newValue[])
{
	if (StrEqual(newValue, "1"))
	{
		for(new i = 1; i <= MaxClients; i++)
		{
			if ( (IsClientInGame(i)) && (IsPlayerAlive(i)) )
				SetEntData(i, collisionGroup, 2, 4, true);
		}
	}
	else
	{
		for(new i = 1; i <= MaxClients; i++)
		{
			if ( (IsClientInGame(i)) && (IsPlayerAlive(i)) )
				SetEntData(i, collisionGroup, 5, 4, true);
		}
	}
}

public Event_PlayerSpawn(Handle:event, const String:name[], bool:dontBroadcast)
{
	if (GetConVarInt(sm_cpsaver_enable) == 1)
	{
		new userid = GetEventInt(event, "userid");
		new client = GetClientOfUserId(userid);
/*
		if (GetConVarInt(sm_cpsaver_standard_hp) != 100)
		{
			SetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), GetConVarInt(sm_cpsaver_standard_hp));
		}
*/
		if (GetConVarInt(sm_cpsaver_noblock_enable))
		{
			SetEntData(client, collisionGroup, 2, 4, true);
		}

		if (GetConVarInt(sm_cpsaver_display_help))
		{
			new Handle:panel = CreatePanel();
			SetPanelTitle(panel, ":: Yd跳跃服坐标缓存系统 ::");

			// Translation formatting
			decl String:serverAdvert1[96];
			Format(serverAdvert1, sizeof(serverAdvert1), "%T", "CheckpointSaver Spawn Ad", client);
			decl String:serverAdvert2[64];
			Format(serverAdvert2, sizeof(serverAdvert2), "%T", "Available Chat Commands", client);
			decl String:serverAdvert3[64];
			Format(serverAdvert3, sizeof(serverAdvert3), "%T", "Enjoy Your Stay", client);

			DrawPanelItem(panel, "-", ITEMDRAW_SPACER);
			DrawPanelItem(panel, serverAdvert1, ITEMDRAW_RAWLINE);
			DrawPanelItem(panel, serverAdvert2, ITEMDRAW_RAWLINE);
			DrawPanelItem(panel, "-", ITEMDRAW_SPACER);
			DrawPanelItem(panel, "* !cdcd 存点菜单", ITEMDRAW_RAWLINE);
			DrawPanelItem(panel, "* !cd   存点", ITEMDRAW_RAWLINE);
			DrawPanelItem(panel, "* !dd   读点", ITEMDRAW_RAWLINE);
			DrawPanelItem(panel, "-", ITEMDRAW_SPACER);
			DrawPanelItem(panel, serverAdvert3, ITEMDRAW_RAWLINE);

			SendPanelToClient(panel, client, PanelHandler, 35);
			CloseHandle(panel);
		}
	}
}

public PanelHandler(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_End)
	{
		CloseHandle(menu);
	}
}

public Event_PlayerHurt(Handle:event, const String:name[], bool:dontBroadcast)
{
	if (GetConVarInt(sm_cpsaver_block_falldamage) == 1 && GetConVarInt(sm_cpsaver_enable) == 1)
	{
		new attacker = GetEventInt(event, "attacker");
		new userid = GetEventInt(event, "userid");
		new target = GetClientOfUserId(userid);

		if ( (attacker == 0) && (IsPlayerAlive(target)) )
		{
			//SetEntData(target, FindSendPropOffs("CBasePlayer", "m_iHealth"), GetConVarInt(sm_cpsaver_standard_hp));
		}
	}
}

public Action:Command_Save(client, args)
{
	Save(client);
	return Plugin_Handled;
}

public Action:Command_Teleport(client, args)
{
	Teleport(client);
	return Plugin_Handled;
}

public Action:Command_CPmenu(client, args)
{
	CPmenu(client);
	return Plugin_Handled;
}

CPmenu(client)
{
	new Handle:menu = CreateMenu(MenuHandler);
	SetMenuTitle(menu, ":: %T ::", "Menu", client);

	// Translation formatting
	decl String:CPmenuT1[64];
	Format(CPmenuT1, sizeof(CPmenuT1), "%T", "Save Location", client);
	decl String:CPmenuT2[64];
	Format(CPmenuT2, sizeof(CPmenuT2), "%T", "Teleport", client);

	AddMenuItem(menu, "cpsave", CPmenuT1);
	AddMenuItem(menu, "cptele", CPmenuT2);

	SetMenuExitButton(menu, true);
	SetMenuOptionFlags(menu, MENUFLAG_NO_SOUND|MENUFLAG_BUTTON_EXIT);
	DisplayMenu(menu, client, MENU_TIME_FOREVER);
}

public MenuHandler(Handle:menu, MenuAction:action, param1, param2)
{
	if (action == MenuAction_Select && GetConVarInt(sm_cpsaver_enable) == 1)
	{
		switch(param2)
		{
			case 0:
			{
				Save(param1);
			}

			case 1:
			{
				Teleport(param1);
			}
		}

		CPmenu(param1);
	}
	else if (action == MenuAction_End)
	{
		CloseHandle(menu);
	}
}

public Event_PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
{
	if (GetConVarInt(sm_cpsaver_enable) == 1 && GetConVarInt(sm_cpsaver_reset_checkpts) == 2)
	{
		new client = GetClientOfUserId(GetEventInt(event, "userid"));
		ResetCpSaves(client);
	}
}

public Action:Event_PlayerTeam(Handle:event, const String:name[], bool:dontBroadcast)
{
	new userid = GetEventInt(event, "userid");
	new client = GetClientOfUserId(userid);
	new team = GetEventInt(event, "team");
	new oldTeam = GetEventInt(event, "oldteam");

	if (GetConVarInt(sm_cpsaver_enable) == 1 && GetConVarInt(sm_cpsaver_reset_checkpts) == 1
		&& team != oldTeam)
	{
		ResetCpSaves(client);
	}
}

Save(client)
{
	if (GetConVarInt(sm_cpsaver_enable) == 1)
	{
		if (IsPlayerAlive(client))
		{
			if (GetEntDataEnt2(client, FindSendPropOffs("CBasePlayer", "m_hGroundEntity")) != -1)
			{
				
				EmitSoundToClient(client, "buttons/blip1.wav");
				PrintToChat(client, MESS, "Location Saved");
				
				//new health = GetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), 4);
				//PrintToChat(client, "测试");
				//new health = GetEntProp(client,Prop_Data,"m_iHealth");
				//new health_buffer = GetEntProp(client,Prop_Data,"m_healthBuffer");
				new health = GetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), 4);
				new Float:health_buffer = GetEntPropFloat(client, Prop_Send, "m_healthBuffer");
				//PrintToChatAll("玩家%N实血量%d，虚血 %f", client, health, health_buffer);
				if (health > GetConVarInt(sm_cpsaver_hpb))
				{
					health -= GetConVarInt(sm_cpsaver_hpb);
					//SetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), health);
					SetEntProp(client,Prop_Data,"m_iHealth", health );
					// 保存
					GetClientAbsOrigin(client, originSaves[client]);
					GetClientAbsAngles(client, angleSaves[client]);
				}
				else if(health_buffer > GetConVarInt(sm_cpsaver_hpb))
				{
					health_buffer -= GetConVarInt(sm_cpsaver_hpb);
					//SetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), health);
					SetEntPropFloat(client,Prop_Send,"m_healthBuffer", health_buffer );
					// 保存
					GetClientAbsOrigin(client, originSaves[client]);
					GetClientAbsAngles(client, angleSaves[client]);
				}
				else
				{
					PrintToChat(client, "血量不足，无法保存");
					PrintHintText(client, "血量不足，无法保存");
				}
				//PrintToChatAll("玩家%N实血量%d，虚血 %f", client, health, health_buffer);
				//PrintToChatAll("\x03保存！！！！！！！！！！！！！！！！！");
			}
			else
			{
				EmitSoundToClient(client, "buttons/button8.wav");
				PrintToChat(client, MESS, "Not On Ground");
			}
		}
		else
		{
			EmitSoundToClient(client, "buttons/button8.wav");
			PrintToChat(client, MESS, "Must Be Alive");
		}
	}
}

Teleport(client)
{
	if (GetConVarInt(sm_cpsaver_enable) == 1)
	{
		if (IsPlayerAlive(client))
		{
			decl Float:Vec[MAXPLAYERS+1][3];
			GetClientAbsOrigin(client,Vec[client]);
			if(GetVectorDistance(originSaves[client], Vec[client]) > 0.00 &&
				!(originSaves[client][0] == NULL_VECTOR[0]
				&& originSaves[client][1] == NULL_VECTOR[1]
				&& originSaves[client][2] == NULL_VECTOR[2]))
			{
				//GetEntProp(client, Prop_Data, "m_iHealth");
				//new health = GetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), 4);
				new health = GetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), 4);
				new Float:health_buffer = GetEntPropFloat(client, Prop_Send, "m_healthBuffer");
				if (health > GetConVarInt(sm_cpsaver_hp))
				{
					health -= GetConVarInt(sm_cpsaver_hp);
					//SetEntData(client, FindSendPropOffs("CBasePlayer", "m_iHealth"), health);
					SetEntProp(client,Prop_Data,"m_iHealth", health );
					TeleportEntity(client, originSaves[client], NULL_VECTOR, NULL_VECTOR);
				}
				else if(health_buffer > GetConVarInt(sm_cpsaver_hp))
				{
					health_buffer -= GetConVarInt(sm_cpsaver_hp);
					SetEntPropFloat(client,Prop_Send,"m_healthBuffer", health_buffer );
					TeleportEntity(client, originSaves[client], NULL_VECTOR, NULL_VECTOR);
				}
				else
				{
					PrintToChat(client, "血量不足，无法还原");
					PrintHintText(client, "血量不足，无法还原");
				}
				//PrintToChatAll("玩家%N实血量%d，虚血 %f", client, health, health_buffer);
				//PrintToChatAll("\x03还原！！！！！！！！！！！！！！！！！");		
			}
			else
			{
				PrintToChat(client, MESS, "No Location Saved");
			}
		}
		else
		{
			EmitSoundToClient(client, "buttons/button8.wav");
			PrintToChat(client, MESS, "Must Be Alive");
		}
	}
}

ResetCpSaves(client)
{
	originSaves[client] = NULL_VECTOR;
	angleSaves[client] = NULL_VECTOR;
}
// 存点效果
public SaveParticle(client)
{

}
// 还原点效果
public TeleportParticle(client)
{
	EmitSoundToAll("level/startwam.wav", client);
	
	ShowParticle(originSaves[client], "electrical_arc_01_system", 5.0);
	
	ShowParticle(originSaves[client], "gas_explosion_main", 5.0);	
	LittleFlower(originSaves[client], MOLOTOV);
} 

public ShowParticle(Float:pos[3], String:particlename[], Float:time)
{
	/* Show particle effect you like */
	new particle = CreateEntityByName("info_particle_system");
	if (IsValidEdict(particle))
	{
		TeleportEntity(particle, pos, NULL_VECTOR, NULL_VECTOR);
		DispatchKeyValue(particle, "effect_name", particlename);
		DispatchKeyValue(particle, "targetname", "particle");
		DispatchSpawn(particle);
		ActivateEntity(particle);
		AcceptEntityInput(particle, "start");
		CreateTimer(time, DeleteParticles, particle);
	}
}

public PrecacheParticle(String:particlename[])
{
	/* Precache particle */
	new particle = CreateEntityByName("info_particle_system");
	if (IsValidEdict(particle))
	{
		DispatchKeyValue(particle, "effect_name", particlename);
		DispatchKeyValue(particle, "targetname", "particle");
		DispatchSpawn(particle);
		ActivateEntity(particle);
		AcceptEntityInput(particle, "start");
		CreateTimer(0.01, DeleteParticles, particle);
	}
}

public Action:DeleteParticles(Handle:timer, any:particle)
{
	/* Delete particle */
	if (IsValidEntity(particle))
	{
		new String:classname[64];
		GetEdictClassname(particle, classname, sizeof(classname));
		if (StrEqual(classname, "info_particle_system", false))
			RemoveEdict(particle);
	}
}

public LittleFlower(Float:pos[3], type)
{
	/* Cause fire(type=0) or explosion(type=1) */
	new entity = CreateEntityByName("prop_physics");
	if (IsValidEntity(entity))
	{
		pos[2] += 10.0;
		if(type == 0)
			/* fire */
			DispatchKeyValue(entity, "model", "models/props_junk/gascan001a.mdl");
		else
			/* explode */
			DispatchKeyValue(entity, "model", "models/props_junk/propanecanister001a.mdl");
		DispatchSpawn(entity);
		SetEntData(entity, GetEntSendPropOffs(entity, "m_CollisionGroup"), 1, 1, true);
		TeleportEntity(entity, pos, NULL_VECTOR, NULL_VECTOR);
		AcceptEntityInput(entity, "break");
	}
}
